﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ZOVNP.Engine;
using ZOVNP.Model.Abstracts;

namespace ZOVNP.WinView
{
    /// <summary> Провайдер </summary>
    /// <remarks> Асинхронно выполняет загрузку движка, т.к. "родной" загрузчик этого не умеет. </remarks>
    public sealed class SQLEngineProvider : ISupportEngine
    {
        /// <summary> Блокировка... </summary>
        private readonly object m_lock = new object();
        /// <summary> Загрузчик движков </summary>
        private readonly ZOVNPEngineLoader m_engineLoader;
        /// <summary> Движок </summary>
        private IEngine m_engine;
        /// <summary> Класс логина </summary>
        private Login m_login;
        /// <summary> Список обработчиков события </summary>
        private CancelEventHandler m_eventChangingHandlerList;
        /// <summary> Список обработчиков события </summary>
        private EventHandler m_eventChangedHandlerList;
        /// <summary> Событие начала изменения текущего движка </summary>
        event CancelEventHandler ISupportEngine.EngineChanging
        {
            add { m_eventChangingHandlerList += value; }
            remove { m_eventChangingHandlerList -= value; }
        }
        /// <summary> Событие окончания изменения текущего движка </summary>
        event EventHandler ISupportEngine.EngineChanged
        {
            add { m_eventChangedHandlerList += value; }
            remove { m_eventChangedHandlerList -= value; }
        }

        /// <summary> Конструктор c логином </summary>
        /// <param name="login"> логин </param>
        public SQLEngineProvider(Login login)
        {
            m_engineLoader = new ZOVNPEngineLoader();
            m_login = login;
            m_login.ConnectionDelegate = CreateEngineInternal;
        }
        /// <summary> Получить текущий движок </summary>
        /// <returns> интерфейс движка IEngine или null, если движок невозможно создать </returns>
        IEngine ISupportEngine.Current
        {
            get
            {
                if (m_engine == null)
                    ((ISupportEngine)this).Create();
                return (m_engine);
            }
        }
        /// <summary> Создать новый движок </summary>
        /// <remarks> При создании нового движка генерятся события </remarks>
        void ISupportEngine.Create()
        {
            CancelEventArgs args = new CancelEventArgs(false);
            if (m_eventChangingHandlerList != null)
            {
                Delegate[] list = m_eventChangingHandlerList.GetInvocationList();
                // пока не закончатся или не дадут отмену
                for (int i = 0; (i < list.Length && !args.Cancel); i++)
                {
                    CancelEventHandler tmp = (CancelEventHandler)list[i];
                    tmp.Invoke(this, args);
                }
            }
            if (!args.Cancel)
            {
                using (Form frm = new LoginForm(m_login))
                    frm.ShowDialog();
            }
        }
        /// <summary> Загрузить движок </summary>
        /// <param name="login"> логин </param>
        /// <remarks> Вызывается в рабочем (не главном) потоке </remarks>
        private void CreateEngineInternal(Login login)
        {
            // Skif_BP_Engine_Loader работает только в один поток
            lock (m_lock)
            {
                SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
                builder.ApplicationName = Application.ProductName;
                builder.IntegratedSecurity = login.IntegratedSecurity;
                if (!login.IntegratedSecurity)
                {
                    builder.UserID = login.UserID;
                    builder.Password = login.Password;
                }
                builder.DataSource = login.CurrentDataSource;
                builder.InitialCatalog = login.CurrentInitialCatalog;

                SetEngineInternal(m_engineLoader.Load(builder.ToString(), login.PasswordNew));

                if (m_eventChangedHandlerList != null)
                    m_eventChangedHandlerList(this, EventArgs.Empty);
            }
        }
        /// <summary> Установить текущий движок </summary>
        /// <param name="engine"> движок </param>
        private void SetEngineInternal(IEngine engine)
        {
            if (m_engine != null)
            {
                //m_engine.Notification -= OnNotification;
                m_engine.PasswordChanging -= OnPasswordChanging;
                m_engineLoader.Unload(m_engine);
            }
            m_engine = engine;
            if (m_engine != null)
            {
                // поток левый, но локаль должна быть "правая"
                //m_engine.SettingsManager.EnvironmentSettings.Locale = System.Threading.Thread.CurrentThread.CurrentCulture;
                m_engine.PasswordChanging += OnPasswordChanging;
                //m_engine.Notification += OnNotification;
            }
        }
        /// <summary> Обработчик события изменения пароля </summary>
        /// <param name="sender"> источник события </param>
        /// <param name="e"> параметры события </param>
        private void OnPasswordChanging(object sender, PasswordChangingEventArgs e)
        {
            using (ChangePasswordForm form = new ChangePasswordForm(m_login))
            {
                e.Cancel = (form.ShowDialog() != DialogResult.OK);
                if (!e.Cancel)
                {
                    e.NewPassword = m_login.PasswordNew;
                    e.OldPassword = m_login.Password;
                }
            }
        }
        /// <summary> Обработчик уведомляющего события </summary>
        /// <param name="sender"> источник события </param>
        /// <param name="e"> параметры события </param>
        //private void OnNotification(object sender, NotificationEventArgs e)
        //{
        //    e.Result = (NotificationResult)new ServiceLocatorHelper().ServiceLocator.GetService<IMessageService>().ShowNotification(e.Message, (MessageServiceMessageBoxButtons)e.AvailableResults);
        //}
    }
}
